package dhadi.use.swing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;

import dhadi.model.Board;
import dhadi.model.Move;
import dhadi.model.Node;
import dhadi.model.Pick;
import dhadi.model.Player;
import dhadi.util.Logger;

public class BoardUi extends JPanel {
	
	private static final long GRAPHICS_REFRESH_RATE_MILLIES = 500;
	public Layout layout;
	public Board board;
	private PickUi movingPick;
	
	/* Notification Message */
	public String notificationMessage = "";
	
	// Node that a Pick moves to after dropping
	private NodeUi currentNode;
	
	// Nodes that a Pick can move
	private List<NodeUi> possibleNodes;
	
	private List<Player> owners = new ArrayList<Player>();
	
	// graphics stuff
	private Image offscreen;
	private Dimension offscreensize;
	private Graphics offgraphics;
	private Thread painterJob;
	
	BoardUi()
	{
	    
	}
	
	public static BoardUi createBoardUi() {
	    final BoardUi boardUi = new BoardUi();
	    Player a = new Player(Board.PLAYER_A);
        Player b = new Player(Board.PLAYER_B);
        
        List<Player> lOwners = new ArrayList<Player>();
        lOwners.add(a);
        lOwners.add(b);
        boardUi.setOwners(lOwners);
        
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {Thread.sleep(1000);} catch(InterruptedException e) {}
                boardUi.board.currentPlayer().makeAMove();
            }
        });
        t.start();
        
        boardUi.board = Board.createBoard(a,b);
        boardUi.layout = new Layout(boardUi.board,boardUi);
        boardUi.setBackground(Color.BLACK);
        boardUi.addMouseMotionListener(new BoardUiMouseMotionListener(boardUi));
        boardUi.addMouseListener(new BoardUiMouseListener(boardUi));
        boardUi.painterJob = new Thread(new PainterJob(boardUi,GRAPHICS_REFRESH_RATE_MILLIES),"PainterJob");
        boardUi.painterJob.start();
        
        return boardUi;
	}
	
	public static BoardUi createBoardUi(Board board) {
	    Player a = board.getPlayerA();
        Player b = board.getPlayerB();
        
        BoardUi boardUi = new BoardUi();
        boardUi.board = board;
        
        List<Player> lOwners = new ArrayList<Player>();
        lOwners.add(a);
        lOwners.add(b);
        boardUi.setOwners(lOwners);
        
        boardUi.layout = new Layout(boardUi.board,boardUi);
        boardUi.setBackground(Color.BLACK);
        boardUi.addMouseMotionListener(new BoardUiMouseMotionListener(boardUi));
        boardUi.addMouseListener(new BoardUiMouseListener(boardUi));
        boardUi.painterJob = new Thread(new PainterJob(boardUi,
                GRAPHICS_REFRESH_RATE_MILLIES),
                "PainterJob");
        boardUi.painterJob.start();
        return boardUi;
	}
	
	class ShowMessageWork implements Runnable {
		private String message;
		private Thread queueAfter;
		public ShowMessageWork(String message, Thread queueAfter) {
			this.message = message;
			this.queueAfter = queueAfter;
		}
		
		public void run() {
			if (this.queueAfter != null) {
				try {
					this.queueAfter.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			BoardUi.this.notificationMessage = message;
			try {
				Thread.sleep(1000*2);
			} catch (InterruptedException e) {}
			BoardUi.this.notificationMessage = "";
		}
	};
	Thread showMessageThread;
	protected void showMessage(String message) {
		
		Logger.log(BoardUi.this.getClass(), message);
		
		// If a message is already displaying wait for it to complete
		// and then show this message.
		// Kind of queuing messages.
		if (showMessageThread != null && showMessageThread.isAlive()) {
			showMessageThread = 
				new Thread(new ShowMessageWork(message,showMessageThread));
		}
		else {
			showMessageThread = 
				new Thread(new ShowMessageWork(message,null));
		}
		showMessageThread.start();
	}

	@Override
	public void paint(Graphics g) {
		dynamicPaint(g);		
	}
	
	public void allowToTest() {
		
		List<Pick> unUsedPicks = this.board.currentPlayer().getUnUsedPicks();
		Pick p = unUsedPicks.size() != 0?unUsedPicks.get(0):null;
		if (p != null) {
			Move m = new Move(p, this.board.currentPlayer());
			List<Node> nodes = this.board.getEmptyNodes();
			m.setToNode(nodes.get(0));
			board.move(m);
		}	
		
		this.repaint();
	}
	
	// paints according to the graphics dimension
	private void dynamicPaint(Graphics g)
	{
		applyModelChanges();
		
		Dimension d = getSize();
		if ((offscreen == null) || (d.width != offscreensize.width)
				|| (d.height != offscreensize.height)) {
			offscreen = createImage(d.width, d.height);
			offscreensize = d;
			if (offgraphics != null) {
				offgraphics.dispose();
			}
			offgraphics = offscreen.getGraphics();
			offgraphics.setClip(g.getClip());
			offgraphics.setFont(getFont());
		}
		
		offgraphics.setColor(getBackground());
		offgraphics.fillRect(0, 0, d.width, d.height);
		
		this.layout.drawLayout(offgraphics);
		g.drawImage(offscreen, 0, 0, null);
	}
	
	private void applyModelChanges() {
		this.layout.applyModelChanges();		
	}
	
	public NodeUi getCurrentNode() {
		return currentNode;
	}
	
	public Board getBoard() {
		return this.board;
	}

	public void setCurrentNode(NodeUi currentNode) {
		if (this.currentNode == null)
		{
			this.currentNode = currentNode;
			if (this.currentNode != null)
				this.currentNode.isCurrentNode = true;
		}	
		else if (!this.currentNode.equals(currentNode))
		{
			this.currentNode.isCurrentNode = false;
			this.currentNode = currentNode;
			if (this.currentNode != null)
				this.currentNode.isCurrentNode = true;
		}
	}

	public static void main(String[] args)
	{
		final BoardUi boardUi = BoardUi.createBoardUi();
		boardUi.showBoard();
		
		
		Thread t = new Thread(new Runnable() {
			public void run() {
				while (true) {
					try {
						Thread.sleep(100);
					} catch(Exception e) {e.printStackTrace();}
					boardUi.allowToTest();
				}
			}
		});
		//t.start();
	}

    public void showPossibleNodes(PickUi pick) {
		this.hidePossibleNodes();
		this.findPossibleNodes(pick);
		for (NodeUi n : this.possibleNodes)
		{
			n.isPossibleNode = true;
		}
	}
	
	private void findPossibleNodes(PickUi pick) {
		if (pick.getPick().isUnused())
		{
			this.possibleNodes = this.layout.getEmptyNodes();
		}
		else if (pick.getPick().isOnboard())
		{
			this.possibleNodes = pick.getNode().getAdjusantEmptyNodes();
		}
	}

	public void hidePossibleNodes() {
		if (this.possibleNodes == null)
			return;
		
		for (NodeUi n : this.possibleNodes)
		{
			n.isPossibleNode = false;
		}
	}
	
	public void setMovingPick(PickUi pick)
	{
		if (pick == null) {
			if (this.movingPick != null) 
			{
				this.movingPick.isHovering = false;
				this.movingPick = null;
			}	
		} else {
			if (!pick.equals(this.movingPick))
			{
				if (this.movingPick != null)
					this.movingPick.isHovering = false;
				this.movingPick = pick;
				this.movingPick.isHovering = true;				
			}
		}	
	}
	
	public PickUi getMovingPick()
	{
		return this.movingPick;
	}
	
	public void setOwners(List<Player> players) {
	    if (!this.owners.isEmpty()) this.owners.clear();
	    this.owners.addAll(players);
	}
	
	public boolean isOwner(Player player) {
	    if (this.owners.isEmpty()) return false;
	    for (Player owner : this.owners) {
	        if (owner.equals(player)) return true;
	    }
	    return false;	    
	}
	
	private JFrame frame;
	public void showBoard() {
        frame = new JFrame("Board Ui");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(500,600));
        frame.add(this);
        frame.setVisible(true);
    }
	
	public void closeBoard() {
	    this.frame.dispose();
	}
}

class PainterJob implements Runnable {
    private long refreshRateMillies = 100; 
    private JPanel panel;
    public PainterJob(JPanel panel, long refreshRateMillies) {
        this.panel = panel;
        this.refreshRateMillies = refreshRateMillies;
    }
    
    public void run() {
        while (true) {
            try {
                Thread.sleep(refreshRateMillies);
            } catch (InterruptedException e) {}
            this.panel.repaint();
        }
    }
}